/* 
 ---------------------------------------------------------------- 
 This is a barebones Xcode Project and Application that is 
 intended to help you in seting up your own projects.
 This source code is by no means a suggestion of how you should
 create your application, but instead just a reference that 
 addresses key OS X specific issues.
 
 Based partially on code from:
 http://www.ogre3d.org/wiki/index.php/MinimalApplication
 ---------------------------------------------------------------- 
 */ 

// Used to determine the fully qualified path to your application
// bundle.
#include "macBundlePath.h"

// Xcode will search a framework for the include automatically
// given you follow the rule of <FRAMEWORK_NAME/HEADER.h>
#include <Ogre/Ogre.h>
#include <Ogre/OgreFrameListener.h>
#include <iostream>

#include "Player.h"
#include "MooseCamera.h"
#include "MooseFlag.h"
#include "InputManager.h"
#include "TextRenderer.h"


// Application State, set false to exit application
bool appGo = true;
Player *player1, *player2;
MooseCamera *camP1, *camP2;
////////////////////////////////////////////////////////////////
// Window Close UPP - "universal procedure pointers"
// -------------------------------------------------
// This code deals with the Carbon event manager, and is here so
// we are notified when the user wants to quit the application.
// Depending on your application, this may be all the
// functionality you need.  However A more robust solution for
// carbon would be to use something like OIS, as the samples do.
////////////////////////////////////////////////////////////////

// This is the window close callback
OSStatus doWindowClose(EventHandlerCallRef nextHandler, 
					   EventRef theEvent, void* userData)
{
	// Set our application state to false, causing an exit on the
	// next update
	appGo = false;
	
	// Propegate the event down the chain
	return CallNextEventHandler (nextHandler, theEvent);
}

// This sets up the EventManager to use our callback when needed
void setupEventHandler()
{
	OSStatus status;
	WindowRef appWindowRef = GetUserFocusWindow();
	EventHandlerUPP appHandler = NewEventHandlerUPP(doWindowClose);
	const EventTypeSpec windowEvents[] =
	{
		{ kEventClassWindow, kEventWindowClose },
		{ kEventClassWindow, kEventWindowCloseAll }
	};
	
	status = InstallWindowEventHandler(appWindowRef, appHandler, GetEventTypeCount(windowEvents), windowEvents, NULL, NULL);
}


////////////////////////////////////////////////////////////////
// class SimpleFrameListener
//////////////////////////////////////////////////////////////// 
class SimpleFrameListener : public Ogre::FrameListener 
{
	// This gets called before the next frame is beeing rendered.
	bool frameStarted(const Ogre::FrameEvent& evt) 
	{         
		// Update inputmanager
		InputManager::getSingletonPtr()->capture();
		
		player1->update(evt.timeSinceLastFrame);
		camP1->update(player1->getVel());
		
		player2->update(evt.timeSinceLastFrame);
		camP2->update(player2->getVel());
		
		TextRenderer::getSingleton().setText("txtGreeting", "P1 : " + StringConverter::toString(player1->getScore()) + "\nP2 : " + StringConverter::toString(player2->getScore()));
		
		return true; 
	} 
	// This gets called at the end of a frame.
	bool frameEnded(const Ogre::FrameEvent& evt) { return appGo; } 
};


////////////////////////////////////////////////////////////////
// Main Class
////////////////////////////////////////////////////////////////

class MainApplication 
	{
	protected:
		
		Ogre::Root* root;
		Ogre::SceneManager* sceneMgr;
		Ogre::RenderWindow* window;
		
	public:
		// Constructor, setup the application. ---------------------------
		MainApplication()
		{
			// This is the path to our stored resources, we copy the
			// resources there with Xcode using the Copy Bundle
			// Resources Phase of the Application Target.
			std::string resourcePath = macBundlePath() + "/Contents/Resources/";
			
			// Initialize Ogre
			// In this case we will not use a plugins.cfg, or ogre.cfg
			root = new Ogre::Root("", "", resourcePath + "Ogre.log");
			
			// Load the plugins
			root->loadPlugin("RenderSystem_GL");
			root->loadPlugin("Plugin_OctreeSceneManager");
			//root->loadPlugin("Plugin_TerrainSceneManager");
			/* Additional Plugins
			 root->loadPlugin("Plugin_ParticleFX");
			 root->loadPlugin("Plugin_BSPSceneManager");
			 root->loadPlugin("Plugin_CgProgramManager");
			 */
			
			// Setup the renderer
			root->setRenderSystem(root->getAvailableRenderers()->front());
			
			// Set up the render system
			root->getRenderSystem()->setConfigOption( "Colour Depth"       , "32"        );
			root->getRenderSystem()->setConfigOption( "FSAA"               , "0"         );
			root->getRenderSystem()->setConfigOption( "Full Screen"        , "No"        );
			root->getRenderSystem()->setConfigOption( "RTT Preferred Mode" , "PBuffer"   );
			root->getRenderSystem()->setConfigOption( "Video Mode"         , "1024 x 768" );
			
			// Create the render window
			window = root->initialise(true, "Test");
			
			// Add resource locations -- looking at folders recursively
			Ogre::ResourceGroupManager::getSingleton().addResourceLocation(resourcePath, std::string("FileSystem"), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, false);
			Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
		}
		
		// Deconstructor, does nothing. ---------------------------
		~MainApplication() 
		{
		}
		
		// Set up the scene manager and populate it with objects. ---------------------------
		void createScene(void)
		{		
			Light *light;
			
			// Create a TerrainSceneManager ----
			sceneMgr = root->createSceneManager(Ogre::ST_EXTERIOR_CLOSE);
			sceneMgr->showBoundingBoxes(false);
			// Set up the terrain ----
			sceneMgr->setWorldGeometry("terrain.cfg");
			
			// Set Ambient light level and other lights and shadows ----
			sceneMgr->setAmbientLight(Ogre::ColourValue(1, 1, 1));
			sceneMgr->setShadowTechnique(SHADOWTYPE_STENCIL_MODULATIVE);
			light = sceneMgr->createLight("LightDir");
			light->setType(Light::LT_DIRECTIONAL);
			light->setAttenuation(8000, 1, 0.5, 0);
			light->setCastShadows(true);
			light->setDiffuseColour(ColourValue(.25,.25,.25));
			light->setSpecularColour(ColourValue(.25,.25,.25));
			light->setDirection(Vector3(0, -1, -1));
			
			
			// Create flags!
			for (int i = 0; i != 25; i++)
			{
				Entity *treeEntity = sceneMgr->createEntity("tree" + StringConverter::toString(i), "tree.mesh");
				SceneNode *treeNode = sceneMgr->getRootSceneNode()->createChildSceneNode("TreeNode" + StringConverter::toString(i));
				treeNode->scale(Vector3(5));
				treeNode->attachObject(treeEntity);
				treeEntity->setCastShadows(true);
				treeNode->setPosition(Math::RangeRandom(100, 12000-100), 100, Math::RangeRandom(100, 12000-100));
			}
			
			MooseFlag *flag = new MooseFlag(sceneMgr);
			
			/*
			Ogre::Entity* world = sceneMgr->createEntity( "world", "Tube01.mesh" );
			Ogre::SceneNode* worldNode = sceneMgr->getRootSceneNode()->createChildSceneNode( "worldNode" );
			worldNode->attachObject(world);
			worldNode->roll(Ogre::Degree(270));
			worldNode->scale(Vector3(10,10,10));
			 */
			// create our player..
			player1 =  new Player(std::string("Helge"), sceneMgr, "kaross.mesh", 0.05, flag, true);
			player1->place(Ogre::Vector3(1000, 800, 1000), Ogre::Quaternion::IDENTITY, Ogre::Vector3::UNIT_Z, Ogre::Vector3::ZERO);
			
			player2 =  new Player(std::string("Snase"), sceneMgr, "kaross.mesh", 0.05, flag, false);
			player2->place(Ogre::Vector3(1200, 800, 1200), Ogre::Quaternion::IDENTITY, Ogre::Vector3::UNIT_Z, Ogre::Vector3::ZERO);
			
			
			// Creaate sky plane
			Plane plane;
			plane.d = 1000;
			plane.normal = Vector3::NEGATIVE_UNIT_Y;
			sceneMgr->setSkyPlane(true, plane, "Examples/CloudySky", 1500, 40, true, 1.5f, 150, 150);
			
			// create camera...
			camP1 = new MooseCamera(sceneMgr, window, player1->getNode(), flag, true);
			camP2 = new MooseCamera(sceneMgr, window, player2->getNode(), flag, false);
			
			new TextRenderer();
			TextRenderer::getSingleton().addTextBox("txtGreeting", "Spelare 1", 10, 10, 100, 20, Ogre::ColourValue::Black);
		}
		
		
		// Start rendering, register frame listener and clean up when done... ---------------------------
		void startRender(void)
		{
			// Setup the Event Handler to call our doWindowClose function
			// when a close event is recieved
			setupEventHandler();
			
			// setup our input handeler
			InputManager *inputMgr = InputManager::getSingletonPtr();
			inputMgr->initialise( window );
			inputMgr->addKeyListener( player1, std::string("Player1KeyListener") );
			inputMgr->addKeyListener( player2, std::string("Player2KeyListener") );
			
			// inputMgr->addMouseListener( this, "ListenerName" );
			// inputMgr->addJoystickListener( this, "ListenerName" );
			
			
			// Install our FrameListener
			Ogre::FrameListener* frameListener = new SimpleFrameListener();
			root->addFrameListener(frameListener);
			
			// Start Rendering
			// Ogre will keep rendering until a frame listener returns false
			// In our case we set up a custom Event UPP for the close events
			root->startRendering();
			
			// Clean up
			delete root;
		}
		
	};


// Main function. ---------------------------
int main(int argc, char **argv)
{
	MainApplication app;
	
	app.createScene();
	app.startRender();
	
	return 0;
}





